package cn.push.embed.service;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v4.app.NotificationCompat;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import http.com.zhy.http.okhttp.callback.Callback;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Random;

import cn.push.embed.config.BroadcastReceiverAction;
import cn.push.embed.core.CommData;
import cn.push.embed.core.bean.MessageBean;
import cn.push.embed.core.http.UserModel;
import cn.push.embed.core.netstatus.NetChangeObserver;
import cn.push.embed.core.netstatus.NetStateReceiver;
import cn.push.embed.core.socket.NioSocketConnection;
import cn.push.embed.core.socket.NioSocketConnectionHandler;
import cn.push.embed.core.socket.Utf8Validator;
import cn.push.embed.core.utils.GetDeviceInfo;
import cn.push.embed.core.utils.Logger;
import cn.push.embed.core.utils.Setting;
import cn.push.embed.protocol.Constant;
import cn.push.embed.protocol.JsonStruct;
import cn.push.embed.protocol.Utils;
import cn.push.embed.utils.CommonUtils;
import okhttp3.Call;
import okhttp3.Response;

public class PushService extends Service {
    private Context mContext = this;
    public static final int ALARM_CLOCK = 1;
    public static final int REGISTER_ALIAS = 2;       //注册别名
    public static final int CANCLE_ALIAS = 3;
    public static final int NET_CONNECT = 4;            //网络连接事件
    public static final int NET_DISCONNECT = 5;         //网络断开连接

    protected NetChangeObserver mNetChangeObserver = null;
    private boolean isConnect = false;     //是否与服务器处于握手成功的连接状态
    public static Handler handler;
    private NioSocketConnection connection = new NioSocketConnection();
    private String alias = "";
    private Setting setting;
    private UserModel userModel;
    private Runnable connectSocketRunable;
    private NioSocketConnectionHandler nioSocketConnectionHandler;
    private static long serverRespondTimestamp = 0;           //服务器响应的时间戳
    private static final long TIMEOUT_INTERVAL = 120 * 1000;   //超时时间120s
    private static byte[] dataBuffer = new byte[4096];
    private static int offset = 0;

    public PushService() {
    }

    /**
     * 检测服务器是否超时
     *
     * @return
     */
    private static boolean isTimeout() {
        return ((System.currentTimeMillis() - serverRespondTimestamp) > TIMEOUT_INTERVAL);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        setting = new Setting(this, getPackageName() + "push");
        userModel = new UserModel(this);
        connectSocketRunable = new Runnable() {
            @Override
            public void run() {
                connect();
            }
        };

        mNetChangeObserver = new NetChangeObserver();
        NetStateReceiver.registerObserver(mNetChangeObserver);
        NetStateReceiver.registerNetworkStateReceiver(this);

        handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                switch (msg.what) {
                    //硬件时钟
                    case ALARM_CLOCK:
                        handleAlarmClock();
                        break;
                    //注册
                    case REGISTER_ALIAS:
                        handlerRegisterAlias(msg.getData().getString("alias"));
                        break;
                    case CANCLE_ALIAS:
                        handlerCancleAlias();
                        break;
                    //网络连接
                    case NET_CONNECT:
                        Logger.d("网络连接");
                        handleAlarmClock();
                        break;
                    //网络断开
                    case NET_DISCONNECT:
                        Logger.d("网络连接断开");
                        closeConnect();
                        break;
                }
                return false;
            }
        });
        nioSocketConnectionHandler = new NioSocketConnectionHandler() {

            @Override
            public void onTextMessage(String payload) {

            }

            @Override
            public void onRawTextMessage(byte[] payload) {

            }

            @Override
            public void onOpen() {
                Logger.d("open");

                JsonStruct jsonStruct = new JsonStruct(GetDeviceInfo.getMetaData(mContext, "PUSH_APPKEY"),
                        "2", GetDeviceInfo.getDevices(PushService.this), GetDeviceInfo.getIdentity(mContext),
                        CommData.AppSecret, PushService.this.alias);
                Logger.d(jsonStruct.toJsonString());
                byte[] data = Utils.getDataFromJson(jsonStruct.toJsonString());
                byte[] result = Utils.getSendPagectFromInt(data, Constant.DataType.LPUSH_HEADER_TYPE_HANDSHAKE);
                connection.sendBinaryMessage(result);
            }

            @Override
            public void onClose(int code, String reason) {
                Logger.d("onClose\n" + "code:" + code + "\n reason:" + reason);
                isConnect = false;

            }

            @Override
            public void onBinaryMessage(byte[] socketData) {
                Logger.d("接收到来自服务器 的信息:"
                        + Arrays.toString(socketData));
                if (dataBuffer.length < offset + socketData.length) {
                    Logger.d("缓冲区溢出");
                } else {
                    Logger.d("offset:" + offset);
                    System.arraycopy(socketData, 0, dataBuffer, offset, socketData.length);
                    offset = socketData.length + offset;
                    Logger.d("offset:" + offset);

                }
                handleSocketData();

            }
        };

        this.alias = setting.loadString("alias");
        if (CommonUtils.isEmpty(this.alias)) {
            this.alias = CommonUtils.EncryptMD5(GetDeviceInfo.getIdentity(this));
        }


    }

    private void handleSocketData() {
        byte[] header1 = new byte[5];
        /**
         * 实现从一个长数组中检索FLAG开头的数据
         */
        for (int i = 0; i < offset - 5; i++) {
            //每次取5个byte的数据
            System.arraycopy(dataBuffer, i, header1, 0, 5);
            if (Arrays.equals(header1, Constant.FLAG)) {
                if (offset >= i + 14) {
                    byte[] lengthByte1 = new byte[4];
                    System.arraycopy(dataBuffer, i + 10, lengthByte1, 0, 4);
                    int length1 = Utils.bytesToInt(lengthByte1);
                    final byte[] payload = new byte[length1 + 14];
                    System.arraycopy(dataBuffer, i, payload, 0, payload.length);
                 /*   offset = offset - (i + payload.length);
                    if (offset<0){
                        offset=0;
                    }
                    if ((offset+i+payload.length)>dataBuffer.length){
                        offset=dataBuffer.length-(i+payload.length);
                    }

                    System.arraycopy(dataBuffer, i + payload.length, dataBuffer, 0, offset);


                    if (offset < 0) {
                        offset = 0;
                    }*/
                    Logger.d("offset:" + offset);

                    if (payload.length > 14) {
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                handlePayload(payload);
                            }
                        }).start();

                    }

                }
            }
        }
        offset = 0;
    }

    /**
     * 处理一包数据
     *
     * @param payload
     */

    private void handlePayload(byte[] payload) {
        byte[] header = new byte[5];
        System.arraycopy(payload, 0, header, 0, 5);
        //判断数据头
        if (Arrays.equals(header, Constant.FLAG)) {
            //握手信息
            if (payload[9] == Constant.DataType.LPUSH_HEADER_TYPE_HANDSHAKE) {
                if (payload[14] == Constant.handShakeType.LPUSH_HANDSHAKE_TYPE_ERR) {
                    Logger.d("handshake err");
                    return;
                } else if (payload[14] == Constant.handShakeType.LPUSH_HANDSHAKE_TYPE_SCREATE_KEY_ERR) {
                    Logger.d("screteKey err");
                } else if (payload[14] == Constant.handShakeType.LPUSH_HANDSHAKE_TYPE_SUCCESS) {
                    Logger.d("handshake success");
                    byte[] result = null;
                    try {
                        result = Utils.getSendPagectFromInt(Utils.stringToByte("createConnection"), Constant.DataType.LPUSH_HEADER_TYPE_CREATE_CONNECTION);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    connection.sendBinaryMessage(result);
                }
                //连接状态
            } else if (payload[9] == Constant.DataType.LPUSH_HEADER_TYPE_CREATE_CONNECTION) {
                if (payload[14] == Constant.connectionType.CONNECTION_ERR) {
                    Logger.d("connection err");
                    return;
                } else if (payload[14] == Constant.connectionType.CONNECTION_OTHER_ONLINE) {
                    Logger.d("other device online");
                    return;
                } else if (payload[14] == Constant.connectionType.CONNECTION_SECCESS) {
                    Logger.d("connection success");
                    isConnect = true;
                    sendPing();
                }

            }
            //接收到心跳
            else if (payload[9] == Constant.DataType.LPUSH_HEADER_TYPE_HREATBEAT) {
                serverRespondTimestamp = System.currentTimeMillis();
            }
            //心跳关闭
            else if (payload[9] == Constant.DataType.LPUSH_HEADER_TYPE_CLOSE) {
                Logger.d("server disconnect");
                CommData.token = "";
                closeConnect();
                // clearAlias();

            }
            //message 信息
            else if (payload[9] == Constant.DataType.LPUSH_HEADER_TYPE_PUSH) {
                byte[] lengthByte = new byte[4];
                System.arraycopy(payload, 10, lengthByte, 0, 4);
                int length = Utils.bytesToInt(lengthByte);
                Logger.d("data number:" + length);
                if (length == (payload.length - 14)) {
                    byte[] messageBytes = new byte[payload.length - 14];
                    System.arraycopy(payload, 14, messageBytes, 0, messageBytes.length);
                    byte messageType = messageBytes[0];
                    if (messageType == Constant.MessageType.LPUSH_FMT_STRING) {
                        Logger.d("message type:string");
                    } else if (messageType == Constant.MessageType.LPUSH_FMT_JSON) {
                        Logger.d("message type:json");
                        byte[] messageJson = new byte[messageBytes.length - 5];
                        System.arraycopy(messageBytes, 5, messageJson, 0, messageJson.length);
                        Utf8Validator utf8Validator = new Utf8Validator();
                        if (!utf8Validator.validate(messageJson)) {
                            Logger.d("message utf8 error");
                        } else {
                            try {
                                String message = new String(messageJson, "UTF-8");
                                Logger.d("data:" + message);
                                //发送接收到信息的通知

                                JSONObject jsonObject = new JSONObject(message);
                                Gson gson = new Gson();
                                Type type = new TypeToken<MessageBean>() {
                                }.getType();
                                MessageBean messageBean = gson.fromJson(message, type);
                                if (messageBean != null) {
                                    Logger.d(messageBean.toString());
                                    String msgId = messageBean.getMsgId();
                                    if (!CommonUtils.isEmpty(msgId)) {
                                        sendDataToServer(msgId);
                                    }
                                    sendBroadcast(mContext, BroadcastReceiverAction.ACTION_MESSAGE_RECEIVED, message);
                                    if (messageBean.getNoticeType().equals(Constant.NoticeType.NOTIFICATION)) {
                                        sendBroadcast(mContext, BroadcastReceiverAction.ACTION_NOTIFICATION_RECEIVED, message);
                                        sendNotification(messageBean, message);
                                    }
                                }

                            } catch (Exception e) {
                                Logger.d("message utf8 error");
                            }
                        }
                    }

                } else {
                    Logger.d("data number error");
                }
            }
        }

    }


    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);

    }

    @Override
    public void onDestroy() {

        NetStateReceiver.removeRegisterObserver(mNetChangeObserver);
        super.onDestroy();

    }

    /***
     * 响应闹钟事件，没有特殊情况下为发送心跳
     * 1、检查用户是否属于注册状态
     * 2、检查手机网络是否可用
     * 3、检查socket是否处于连接状态
     * 4、发送心跳
     */
    private void handleAlarmClock() {
        if (!CommData.IS_DEVICE) {
            registerDevice();
        }
        //用户是否已经设置过昵称
        if (CommonUtils.isEmpty(this.alias)) {
            this.alias = setting.loadString("alias");
        }
        if (CommonUtils.isEmpty(this.alias)) {
            Logger.d("alias is empty");
            return;
        }
        //检查网络是否可用
        if (!GetDeviceInfo.isNetworkAvailable(this)) {
            Logger.d("net work error");
            return;
        }
        //发送心跳
        if (this.isConnect && (!isTimeout())) {
            sendPing();
        } else {
            //重连
            closeConnect();
            if ((!CommonUtils.isEmpty(CommData.ipPort)) && (!CommonUtils.isEmpty(CommData.token))) {
                this.serverAlias(this.alias);
            } else {
                this.registerAlias(this.alias);
            }
        }


    }

    private void startConnect() {
        new Thread(connectSocketRunable).start();
    }

    private void connect() {
        if (CommonUtils.isEmpty(CommData.ipPort)) {
            return;
        }
        connection.connect(CommData.ipPort, nioSocketConnectionHandler);
    }

    private void closeConnect() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                connection.disConnect();
                PushService.this.isConnect = false;
            }
        }).start();

    }


    private void handlerRegisterAlias(String alias) {
        if (CommonUtils.isEmpty(this.alias)) {
            logoutAlias(this.alias);
            this.alias = "";
            setting.saveString("alias", "");
            closeConnect();
        }

        if (CommonUtils.isEmpty(this.alias)) {
            setting.saveString("alias", alias);
            this.alias = alias;
            //去服务器进行注册
            registerAlias(this.alias);
        } else {
            if (this.alias.equals(alias)) {
                return;
            } else {
                //关闭现有连接
                logoutAlias(this.alias);
                closeConnect();
                setting.saveString("alias", alias);
                this.alias = alias;
                //去服务器注册
                registerAlias(this.alias);
            }
        }
    }

    private void handlerCancleAlias() {
        logoutAlias(this.alias);
        this.alias = "";
        setting.saveString("alias", "");
        closeConnect();

    }

    /**
     * 首次注册
     *
     * @param alias
     */
    private void registerAlias(String alias) {
        userModel.register(alias, new Callback<String>() {
            @Override
            public String parseNetworkResponse(Response response, int id) throws Exception {
                String body = response.body().string();
                return body;
            }

            @Override
            public void onError(Call call, Exception e, int id) {
                Logger.d(e.getMessage());
            }

            @Override
            public void onResponse(String response, int id) {
                Logger.d(response);
                int code = getCode(response);
                JSONObject jsonObject = null;
                try {
                    jsonObject = new JSONObject(response);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                switch (code) {
                    //成功
                    case Constant.HttpCode.SUCCESS:
                        try {
                            String data = jsonObject.optString("data");
                            JSONObject dataJson = new JSONObject(data);
                            String servierId = dataJson.optString("ServerId");
                            if (CommonUtils.isEmpty(servierId)) {
                                return;
                            }
                            CommData.ipPort = servierId;
                            String token = dataJson.getString("Token");
                            if (CommonUtils.isEmpty(token)) {
                                return;
                            }
                            CommData.token = token;
                            String isSso = dataJson.getString("IsSso");
                            if (!CommonUtils.isEmpty(token)) {
                                CommData.isSSo = isSso;
                            }
                            startConnect();
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                        break;
                    //应用未注册
                    case Constant.HttpCode.APP_NO_REGISTER:
                        clearAlias(jsonObject);
                        break;
                    //重连没有注册
                    case Constant.HttpCode.NEVER_RESITER:
                        clearAlias(jsonObject);
                        break;
                    //但连接状态下重复登录
                    case Constant.HttpCode.OVER_CONNECT:
                        clearAlias(jsonObject);
                        break;
                    //参数错误
                    case Constant.HttpCode.PARAMETERS_ERR:
                        clearAlias(jsonObject);
                        break;
                }

            }
        });
    }

    public void registerDevice() {
        userModel.device(new Callback<String>() {
            @Override
            public String parseNetworkResponse(Response response, int id) throws Exception {
                String body = response.body().string();
                return body;
            }

            @Override
            public void onError(Call call, Exception e, int id) {
                Logger.i(e.getMessage());
            }

            @Override
            public void onResponse(String response, int id) {
                Logger.d("server:" + response);
                int code = getCode(response);
                if (code == Constant.HttpCode.SUCCESS) {
                    CommData.IS_DEVICE = true;
                }

            }
        });
    }


    public void serverAlias(String alias) {
        userModel.server(alias, new Callback<String>() {
            @Override
            public String parseNetworkResponse(Response response, int id) throws Exception {
                String body = response.body().string();
                return body;
            }

            @Override
            public void onError(Call call, Exception e, int id) {
                Logger.i(e.getMessage());
            }

            @Override
            public void onResponse(String response, int id) {
                Logger.d("server:" + response);
                int code = getCode(response);
                JSONObject jsonObject = null;
                try {
                    jsonObject = new JSONObject(response);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                switch (code) {
                    //成功
                    case Constant.HttpCode.SUCCESS:

                        String servierId = jsonObject.optString("data");
                        if (CommonUtils.isEmpty(servierId)) {
                            return;
                        }
                        CommData.ipPort = servierId;
                        startConnect();
                        break;
                    //应用未注册
                    case Constant.HttpCode.APP_NO_REGISTER:
                        clearAlias(jsonObject);
                        break;
                    //重连没有注册
                    case Constant.HttpCode.NEVER_RESITER:
                        clearAlias(jsonObject);
                        break;
                    //但连接状态下重复登录
                    case Constant.HttpCode.OVER_CONNECT:
                        clearAlias(jsonObject);
                        break;
                    //参数错误
                    case Constant.HttpCode.PARAMETERS_ERR:
                        clearAlias(jsonObject);
                        break;
                }
            }
        });
    }

    private void logoutAlias(String alias) {
        if (CommonUtils.isEmpty(alias)) return;
        userModel.logout(alias, new Callback<String>() {
            @Override
            public String parseNetworkResponse(Response response, int id) throws Exception {
                String body = response.body().string();
                return body;
            }

            @Override
            public void onError(Call call, Exception e, int id) {
                Logger.d(e.getMessage());
            }

            @Override
            public void onResponse(String response, int id) {
                Logger.d(response);
            }


        });
    }


    private int logRequest(String url, HashMap<String, String> params) {
        Random random = new Random();
        final int randomTag = random.nextInt(0xffff);
        Logger.d("url", "url " + randomTag + ":Request " + url + params.toString());
        return randomTag;
    }

    private void logOnReponse(String response, int randomTag) {
        Logger.d("url", "url " + randomTag + ":Response  " + response);
    }

    private int logRequest(String url) {
        Random random = new Random();
        final int randomTag = random.nextInt(0xffff);
        Logger.d("url", "url " + randomTag + ":Request " + url);
        return randomTag;
    }


    private int getCode(String json) {
        try {
            JSONObject jsonObject = new JSONObject(json);
            int code = jsonObject.getInt("code");
            return code;
        } catch (JSONException e) {
            return -1;
        }
    }

    /**
     * 错误码时，清除本地昵称，打印错误日志
     */
    private void clearAlias(JSONObject jsonObject) {
        String msg = jsonObject.optString("msg");
        if (!CommonUtils.isEmpty(msg)) {
            Logger.d(msg);
        }
        this.setting.saveString("alias", "");
        Logger.d("alias clear");
        closeConnect();
    }


    private void sendPing() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                connection.sendPingFrame();
            }
        }).start();
    }


    private void sendDataToServer(final String message) {
        Logger.d("消息确认:" + message);
        new Thread(new Runnable() {
            @Override
            public void run() {
                byte[] data = Utils.getDataFromString(message);
                byte[] result = Utils.getSendPagectFromInt(data, Constant.DataType.LPUSH_HEADER_TYPE_PUSH);
                connection.sendBinaryMessage(result);
                Logger.d("发送数据:"
                        + Arrays.toString(result));
            }
        }).start();
    }

    private void sendBroadcast(final Context mContext, final String action, final String content) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                CommonUtils.sendBroadcast(mContext, action, content);
            }
        }).start();

    }

    private void sendNotification(MessageBean messageBean, String message) {
        Random r = new Random();
        int id = Math.abs(r.nextInt());
        Logger.d("notifition id:" + id);
        Intent intent = new Intent(BroadcastReceiverAction.ACTION_NOTIFICATION_OPENED);
        intent.addCategory(getPackageName());
        intent.putExtra("MessageBean", message);
        //也就是发送了action 为"ELITOR_CLOCK"的intent
        PendingIntent pi = PendingIntent.getBroadcast(mContext, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        //获取NotificationManager实例
        NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //实例化NotificationCompat.Builde并设置相关属性
        NotificationCompat.Builder builder = null;
        if (CommonUtils.isEmpty(messageBean.getImage())) {
            builder = getBuilder(messageBean, pi, null);
        } else {
            builder = getBuilder(messageBean, pi, CommonUtils.getbitmap(messageBean.getImage()));
        }


        notifyManager.notify(id, builder.build());
    }

    private NotificationCompat.Builder getBuilder(MessageBean messageBean, PendingIntent pi, Bitmap image) {
        if (image == null) {
            return new NotificationCompat.Builder(this)
                    //设置小图标
                    .setSmallIcon(CommData.iconSourceId)
                    .setContentIntent(pi)
                    //设置通知标题
                    .setContentTitle(messageBean.getTitle())
                    //设置通知内容
                    .setContentText(messageBean.getContent())
                    .setTicker(messageBean.getContent())
                    .setAutoCancel(true)
                    //设置声音，震动
                    .setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.FLAG_AUTO_CANCEL);
        } else {
            return new NotificationCompat.Builder(this)
                    //设置小图标
                    .setSmallIcon(CommData.iconSourceId)
                    .setLargeIcon(image)
                    .setContentIntent(pi)
                    //设置通知标题
                    .setContentTitle(messageBean.getTitle())
                    //设置通知内容
                    .setContentText(messageBean.getContent())
                    .setTicker(messageBean.getContent())
                    .setAutoCancel(true)
                    //设置声音，震动
                    .setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.FLAG_AUTO_CANCEL);
        }

    }


}
